package com.tricky.tron.trc20.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tricky.tron.trc20.common.MessageResult;
import com.tricky.tron.trc20.entity.Account;
import com.tricky.tron.trc20.feign.TronFullNodeFeign;
import com.tricky.tron.trc20.feign.dt.EasyTransferByPrivate;
import com.tricky.tron.trc20.feign.dt.GetTransactionSign;
import com.tricky.tron.trc20.feign.dt.TriggerSmartContract;
import com.tricky.tron.trc20.feign.res.HttpRes;
import com.tricky.tron.trc20.utils.ByteArray;
import com.tricky.tron.trc20.utils.TronUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;


@Service
public class Trc20Service {
    private static Logger logger = LoggerFactory.getLogger(Trc20Service.class);

    @Value("${tron.contract-address}")
    private String contractAddress;//hex格式

    @Value("${tron.address}")
    private String address;//发币地址 hex格式

    @Value("${tron.private-key}")
    private String privateKey;//私钥

    //token的精度  就是小数点后面有多少位小数 然后1后面加多少个0就可以
    private static final BigDecimal decimal = new BigDecimal("1000000");

    @Autowired
    private TronFullNodeFeign feign;

    /**
     * 发送trc20交易  返回交易id
     *
     * @param toAddress 收币地址
     * @param amount    转出数量
     * @param remark    备注
     * @return
     */
    public String sendTrc20Transaction(String toAddress, String amount, String remark) {
        try {
            String hexAddress = toAddress;
            if (toAddress.startsWith("T")) {
                hexAddress = TronUtils.toHexAddress(toAddress);
            }
            if (StringUtils.isEmpty(hexAddress)) {
                logger.error("转账失败:收款地址为空");
                return null;
            }
            if (StringUtils.isEmpty(amount)) {
                logger.error("转账失败:额度为空");
                return null;
            }
            BigDecimal a = new BigDecimal(amount);
            if (a.compareTo(BigDecimal.ZERO) <= 0) {
                logger.error("转账失败:额度不符合规则 " + amount);
                return null;
            }
            if (remark == null) {
                remark = "";
            }
            String params = hexAddress + "@" + amount + "@" + remark;
            TriggerSmartContract.Param param = createTriggerSmartContractParam();
            param.setFunction_selector("transfer(address,uint256)");
            String addressParam = addZero(hexAddress, 64);
            String amountParam = addZero(new BigDecimal(amount).multiply(decimal).toBigInteger().toString(16), 64);
            param.setParameter(addressParam + amountParam);
            logger.info("创建交易参数:" + JSONObject.toJSONString(param));
            TriggerSmartContract.Result obj = feign.triggerSmartContract(param);
            logger.info("创建交易结果:" + JSONObject.toJSONString(obj));
            if (!obj.isSuccess()) {
                logger.error("创建交易失败|" + params);
                return null;
            }
            //交易签名
            GetTransactionSign.Param signParam = new GetTransactionSign.Param();
            TriggerSmartContract.Transaction transaction = obj.getTransaction();
            transaction.getRaw_data().put("data", ByteArray.toHexString(remark.getBytes()));
            signParam.setTransaction(transaction);
            signParam.setPrivateKey(privateKey);
            logger.info("签名交易参数:" + JSONObject.toJSONString(signParam));
            Object dt = feign.getTransactionSign(signParam);
            logger.info("签名交易结果:" + JSONObject.toJSONString(dt));
            //广播交易
            if (dt != null) {
                logger.info("广播交易参数:" + JSONObject.toJSONString(dt));
                JSONObject rea = feign.broadcastTransaction(dt);
                logger.info("广播交易结果:" + JSONObject.toJSONString(rea));
                if (rea != null) {
                    Object result = rea.get("result");
                    if (result instanceof Boolean) {
                        if ((boolean) result) {
                            return (String) rea.get("txid");
                        }
                    }
                }
            }
        } catch (Throwable t) {
            logger.error(t.getMessage(), t);
        }
        return null;
    }

    /**
     * 创建智能合约参数
     *
     * @return
     */
    private TriggerSmartContract.Param createTriggerSmartContractParam() {
        TriggerSmartContract.Param tscParam = new TriggerSmartContract.Param();
        tscParam.setOwner_address(address);
        tscParam.setContract_address(contractAddress);
        tscParam.setFee_limit(1000000000L);
        return tscParam;
    }

    /**
     * 补充0到64个字节
     *
     * @param dt
     * @return
     */
    private String addZero(String dt, int length) {
        StringBuilder builder = new StringBuilder();
        final int count = length;
        int zeroAmount = count - dt.length();
        for (int i = 0; i < zeroAmount; i++) {
            builder.append("0");
        }
        builder.append(dt);
        return builder.toString();
    }

    /**
     * 插叙额度
     *
     * @param contract 合约地址
     * @param address  查询地址
     * @return
     */
    public BigInteger balanceOf(String contract, String address) {
        String hexAddress = address;
        if (address.startsWith("T")) {
            hexAddress = TronUtils.toHexAddress(address);
        }
        String hexContract = contract;
        if (contract.startsWith("T")) {
            hexContract = TronUtils.toHexAddress(contract);
        }
        TriggerSmartContract.Param param = new TriggerSmartContract.Param();
        param.setContract_address(hexContract);
        param.setOwner_address(hexAddress);
        param.setFunction_selector("balanceOf(address)");
        String addressParam = addZero(hexAddress.substring(2), 64);
        param.setParameter(addressParam);
        TriggerSmartContract.Result result = feign.triggerSmartContract(param);
        if (result != null && result.isSuccess()) {
            String value = result.getConstantResult(0);
            if (value != null) {
                return new BigInteger(value, 16);
            }
        }
        return BigInteger.ZERO;
    }

    private String castHexAddress(String address) {
        if (address.startsWith("T")) {
            return TronUtils.toHexAddress(address);
        }
        return address;
    }

    public MessageResult feeAmtTransfer(Account account, String feeAmt) {
        //发起矿工费转账
       String txId = this.easytransferbyprivate(account.getAddress(),feeAmt,"矿工费转账");
       logger.info("create new amount={},feeAmt={},txId={}", account, feeAmt,txId);
       return new MessageResult(0,"提交成功");

    }

    /**
     * 代币转账  trc20
     *
     * @param contract
     * @param fromAddress
     * @param privateKey  fromAddress的私钥
     * @param amount
     * @param toAddress
     * @param remark
     * @return
     */
    public String sendTokenTransaction(String contract, String fromAddress, String privateKey, String amount, String toAddress, String remark) {
        try {
            String hexFromAddress = castHexAddress(fromAddress);
            String hexToAddress = castHexAddress(toAddress);
            String hexContract = castHexAddress(contract);

            BigInteger a = new BigInteger(amount);
            if (a.compareTo(BigInteger.ZERO) <= 0) {
                logger.error("转账失败:额度不符合规则 " + amount);
                return null;
            }
            if (remark == null) {
                remark = "";
            }
            TriggerSmartContract.Param param = new TriggerSmartContract.Param();
            param.setOwner_address(hexFromAddress);
            param.setContract_address(hexContract);
            param.setFee_limit(1000000000L);
            param.setFunction_selector("transfer(address,uint256)");
            String addressParam = addZero(hexToAddress, 64);
            String amountParam = addZero(a.toString(16), 64);
            param.setParameter(addressParam + amountParam);
            logger.info("创建交易参数:" + JSONObject.toJSONString(param));
            TriggerSmartContract.Result obj = feign.triggerSmartContract(param);
            logger.info("创建交易结果:" + JSONObject.toJSONString(obj));
            if (!obj.isSuccess()) {
                logger.error("创建交易失败");
                return null;
            }
            //交易签名
            GetTransactionSign.Param signParam = new GetTransactionSign.Param();
            TriggerSmartContract.Transaction transaction = obj.getTransaction();
            transaction.getRaw_data().put("data", ByteArray.toHexString(remark.getBytes()));
            signParam.setTransaction(transaction);
            signParam.setPrivateKey(privateKey);
            logger.info("签名交易参数:" + JSONObject.toJSONString(signParam));
            Object dt = feign.getTransactionSign(signParam);
            logger.info("签名交易结果:" + JSONObject.toJSONString(dt));
            //广播交易
            if (dt != null) {
                logger.info("广播交易参数:" + JSONObject.toJSONString(dt));
                JSONObject rea = feign.broadcastTransaction(dt);
                logger.info("广播交易结果:" + JSONObject.toJSONString(rea));
                if (rea != null) {
                    Object result = rea.get("result");
                    if (result instanceof Boolean) {
                        if ((boolean) result) {
                            return (String) rea.get("txid");
                        }
                    }
                }
            }
        } catch (Throwable t) {
            logger.error(t.getMessage(), t);
        }
        return null;
    }


    /**
     * 查询tron币数量
     *
     * @param address
     * @return
     */
    public BigDecimal balanceOfTron(String address) {
        final BigDecimal decimal = new BigDecimal("1000000");
        final int accuracy = 6;//六位小数
        Map<String, Object> param = new HashMap<>();
        param.put("address", castHexAddress(address));
        JSONObject obj = feign.getAccount(param);
        if (obj != null || obj.size() != 0) {
            BigInteger balance = obj.getBigInteger("balance");
            return new BigDecimal(balance).divide(decimal, accuracy, RoundingMode.FLOOR);
        }
        return BigDecimal.ZERO;
    }

    public String easytransferbyprivate( String toAddress,String privateKey, String amount){
        EasyTransferByPrivate.Param param = new EasyTransferByPrivate.Param();
        param.setAmount(new BigDecimal(amount));
        param.setPrivateKey(privateKey);
        param.setToAddress(toAddress);
        JSONObject obj = feign.easyTransferByPrivate(param);
        System.out.println(obj);
        if (obj != null) {
            HttpRes httpRes = JSON.toJavaObject(obj, HttpRes.class);
            if (httpRes.getResult().getResult()) {
                return httpRes.getTransaction().getTxID();
            }
        }
        return null;

    }

    /**
     * 生成一个地址
     *
     * @return
     */
    public JSONObject createAddress() {
        return feign.generateAddress();
    }
}
